package queue

// 代码主要参考的是https://github.com/bobohume/gonet

//当前为多生产者，单消费者的并发队列
//这个队列需要实现多协程并发处理，并且是无锁队列
//主要是多个协程生产，单个协程消费
import (
	"sync/atomic"
	"unsafe"
)

type node[T any] struct {
	next *node[T]
	val  T
}

type MPSCQueue[T any] struct {
	head, tail *node[T]
	_nil       T
	len        int64
}

func New[T any]() *MPSCQueue[T] {
	q := &MPSCQueue[T]{}
	stub := &node[T]{}
	q.head = stub
	q.tail = stub
	q.len = 0
	return q
}

func (q *MPSCQueue[T]) Enqueue(x T) {
	n := new(node[T])
	n.val = x
	// current producer acquires head node
	prev := (*node[T])(atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(&q.head)), unsafe.Pointer(n)))

	// release node to consumer
	atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&prev.next)), unsafe.Pointer(n))
	atomic.AddInt64(&q.len, 1)
}

func (q *MPSCQueue[T]) Dequeue() T {
	tail := q.tail
	next := (*node[T])(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&tail.next)))) // acquire
	if next != nil {
		q.tail = next
		v := next.val
		atomic.AddInt64(&q.len, -1) // 减少长度计数器
		return v
	}
	return q._nil
}

func (q *MPSCQueue[T]) Empty() bool {
	return atomic.LoadInt64(&q.len) == 0
}

func (q *MPSCQueue[T]) Len() int {
	return int(atomic.LoadInt64(&q.len))
}
